﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using System;
using System.Reflection;
using System.Linq;
using Extensions;
using System.Text;

namespace TC
{
    public class TreeNode
    {
        #region 状态相关的变量
        public bool isRoot { get => (parent == null); }
        public bool isLeafNode { get => (children.Count <= 0); }
        public bool isDisabled = false; // 根据Graph、Port类型是否不进行显示
        public bool isFolded = true; //是否折叠
        public bool isMatching = true; //当前字符串或子节点字符串是否匹配搜索
        #endregion

        #region 构造相关的变量
        private Type target;
        public TreeNode parent;
        public List<TreeNode> children = new List<TreeNode>(); // 直属children
        public string name;
        private ValueWrap contextVar;
        public int depth;
        #endregion

        #region 绘制相关的变量
        private GUIStyle itemBgStyle = new GUIStyle("box");
        private GUIStyle itemToggleStyle = StyleProvider.MenuItemBtn;
        private GUIStyle itemLeafStyle = new GUIStyle("label");
        private GUIStyle itemNameStyle = new GUIStyle("label");

        public float toggleLength = 20;
        public float indentSizeX { get => toggleLength * depth; }
        #endregion

        #region 构造相关的函数

        public TreeNode(string name, Type type = null, TreeNode parent = null, ValueWrap variable = default)
        {
            this.name = name;
            target = type;
            this.parent = parent;
            contextVar = variable;
            depth = (parent != null) ? (parent.depth + 1) : -1;
            InitGUIStyle();
        }

        public void AddChild(TreeNode treeNode)
        {
            children.Add(treeNode);
        }

        public void RemoveChild(TreeNode treeNode)
        {
            children.Remove(treeNode);
        }

        public List<TreeNode> GetAllChildren()
        {
            List<TreeNode> treeNodes = new List<TreeNode>();
            foreach (var child in children)
            {
                treeNodes.Add(child);
                var subChildren = child.GetAllChildren();
                if (subChildren != null) { treeNodes = treeNodes.Concat(subChildren).ToList(); }
            }
            return treeNodes;
        }
        #endregion

        #region 状态相关的函数
        public void SetSearchMatchCase(string searchStr = "")
        {
            foreach (var child in children)
            {
                child.SetSearchMatchCase(searchStr);
            }
            if (name.IndexOf(searchStr, 0, StringComparison.CurrentCultureIgnoreCase) != -1 || searchStr == "") { isMatching = true; }
            else { isMatching = false; }
            foreach (var child in children)
            {
                if (child.isMatching)
                {
                    isMatching = true;
                    // 搜索空串,子文件夹不打开
                    if (searchStr != "") { isFolded = false; }
                    else { isFolded = true; }
                    break;
                }
            }
        }

        public void SetPortMatchCase(Port port)
        {
            FieldInfo portInfo;
            if (isLeafNode && target != null)
            {
                // TODO get object 的 can Connect 是否需要变动？
                if (port.CanConnect(target, out portInfo)) { isDisabled = false; }
                else { isDisabled = true; }
            }
            else
            {
                foreach (var child in children)
                {
                    child.SetPortMatchCase(port);
                }
                isDisabled = true;
                foreach (var child in children)
                {
                    isDisabled = child.isDisabled;
                    if (!isDisabled) { break; }
                }
            }
        }

        public void ResetState()
        {
            isMatching = true;
            isDisabled = false;
            isFolded = true;
            foreach (var child in children) { child.ResetState(); }
        }
        #endregion

        #region 绘制相关的函数

        private void InitGUIStyle()
        {
            itemBgStyle.stretchWidth = true;

            itemNameStyle.fontSize = 14;
            itemNameStyle.alignment = TextAnchor.MiddleLeft;
            itemNameStyle.normal.textColor = EditorGUIUtility.isProSkin ? Color.white : Color.black;
            itemNameStyle.stretchWidth = true;

            itemToggleStyle.fixedWidth = toggleLength;
            itemToggleStyle.fixedHeight = itemNameStyle.CalcSize(new GUIContent(name)).y;

            itemLeafStyle.alignment = TextAnchor.MiddleCenter;
            itemLeafStyle.normal.textColor = EditorGUIUtility.isProSkin ? 
                new Color(78.0f / 255, 176.0f / 255, 116.0f / 255) : Color.black;
            itemLeafStyle.fixedWidth = toggleLength;
            itemLeafStyle.fixedHeight = itemToggleStyle.fixedHeight;
        }

        public bool CanDraw()
        {
            return !isRoot && isMatching && !isDisabled;
        }

        public void OnDraw()
        {
            if (CanDraw()) 
            {
                GUILayout.BeginHorizontal(itemBgStyle);
                GUILayout.Space(indentSizeX);
                if (!isLeafNode)
                { isFolded = GUILayout.Toggle(isFolded, "", itemToggleStyle); }
                else
                { GUILayout.Box("☀", itemLeafStyle); }
                if (GUILayout.Button(name, itemNameStyle))
                {
                    if (isLeafNode)
                    {
                        if (contextVar != default)
                        { TreeViewMenu.instance.NodeClickHandler(target, contextVar); }
                        else { TreeViewMenu.instance.NodeClickHandler(target); }
                    }
                    else { isFolded = !isFolded; }
                }
                GUILayout.EndHorizontal();
            }

            if (!isFolded || isRoot)
            {
                foreach (var child in children)
                {
                    child.OnDraw();
                }
            }
        }
        #endregion

        public void OnEvent()
        {
            // 自己的事件检测
            foreach (var child in children)
            {
                child.OnEvent();
            }
        }

        public override string ToString()
        {
            string returnStr = " ".Repeat(depth * 4) + name;
            returnStr += "\n";
            foreach (var child in children)
            {
                returnStr += child.ToString();
            }
            return returnStr;
        }

    }

    public class TreeViewMenu : PopupWindowContent
    {
        #region 构建树
        private string rootName = "root";
        public static TreeViewMenu instance = new TreeViewMenu();
        private List<Type> allTypes = new List<Type>();
        public TreeNode root;
        private Dictionary<string, TreeNode> treeNodeDic = new Dictionary<string, TreeNode>();
        private GraphSurface currentGraphSurf;
        private Graph currentGraph;


        public TreeViewMenu()
        {
            Assembly assembly = Assembly.Load("Assembly-CSharp");
            foreach (var type in assembly.GetExportedTypes())
            {
                if (type.IsDefined(typeof(MenuAttribute)))
                {
                    allTypes.Add(type);
                }
            }
            root = new TreeNode(rootName);
            foreach (var type in allTypes)
            {
                AddTreeNode(type.GetCustomAttribute<MenuAttribute>().menu, type);
            }
            InitGUIStyle();
        }

        public void AddTreeNode(string path, Type type, ValueWrap variable = default)
        {
            string[] nodeNames = path.Split('/');
            TreeNode lastNode = root;
            for (int i = 0; i < nodeNames.Length; i++)
            {
                if (!treeNodeDic.ContainsKey(nodeNames[i]))
                {
                    //添加对应的type
                    TreeNode newNode;
                    if (i == (nodeNames.Length - 1))
                    {
                        newNode = new TreeNode(nodeNames[i], type, lastNode, variable);
                    }
                    else
                    {
                        newNode = new TreeNode(nodeNames[i], null, lastNode, variable);
                    }
                    treeNodeDic.Add(nodeNames[i], newNode);
                    lastNode.AddChild(newNode);
                    lastNode = newNode;
                }
                else
                {
                    lastNode = treeNodeDic[nodeNames[i]];
                }
            }
        }

        public void RemoveTreeNode(string nodeName)
        {
            TreeNode treeNode;
            if (treeNodeDic.TryGetValue(nodeName, out treeNode))
            {
                List<TreeNode> allChildren = treeNode.GetAllChildren();
                foreach (var child in allChildren)
                {
                    treeNodeDic.Remove(child.name);
                }
                treeNode.parent.RemoveChild(treeNode);
                treeNodeDic.Remove(nodeName);
            }
        }

        public TreeNode GetTreeNode(string nodeName)
        {
            TreeNode treeNode;
            treeNodeDic.TryGetValue(nodeName, out treeNode);
            return treeNode;
        }

        public bool NotEmpthTree()
        {
            foreach (var item in treeNodeDic)
            {
                if (item.Value.CanDraw()) { return true; }
            }
            return false;
        }

        #endregion

        #region 绘制
        private GUIStyle titleStyle = new GUIStyle("box");
        private GUIStyle searchBarStyle = new GUIStyle("ToolbarSeachTextField");
        private GUIStyle searchCancelStyle = new GUIStyle("ToolbarSeachCancelButton");
        private GUIStyle scrollViewStyle = new GUIStyle("label");

        public Vector2 position; //弹出面板在父级EditorWindow中的位置
        string searchStr = "";
        private string searchBarContentName = "searchContent";
        Vector2 scrollPosition = new Vector2(0, 0);

        public override Vector2 GetWindowSize() { return new Vector2(300, 300);}

        private void InitGUIStyle()
        {
            titleStyle.stretchWidth = true;
            titleStyle.alignment = TextAnchor.MiddleCenter;
            titleStyle.richText = true;
            titleStyle.fixedHeight = 30;
            titleStyle.fontSize = 26;
            titleStyle.normal.textColor = new Color(225.0f / 255, 150.0f / 255, 14.0f / 255);
            titleStyle.margin.left = titleStyle.margin.right = 0;

            searchBarStyle.fontSize = 10;
            searchBarStyle.fixedHeight = 16;

            searchCancelStyle.fixedHeight = 16;

            scrollViewStyle.stretchWidth = true;
        }

        public override void OnGUI(Rect rect)
        {
            OnEvent();

            GUILayout.BeginArea(rect);
            GUI.Box(rect, "");
            GUILayout.Box("Create A Node", titleStyle);

            GUILayout.BeginHorizontal();
            GUI.SetNextControlName(searchBarContentName);
            searchStr = GUILayout.TextField(searchStr, searchBarStyle);
            if (GUILayout.Button("", searchCancelStyle))
            {
                root.SetSearchMatchCase();
                searchStr = "";
                GUI.FocusControl(null);
            }
            GUILayout.EndHorizontal();

            if (NotEmpthTree())
            {
                scrollPosition = GUILayout.BeginScrollView(scrollPosition, scrollViewStyle);
                root.OnDraw();
                GUILayout.EndScrollView();
            }
            else
            {
                GUILayout.Label("No nodes matches..");
            }

            GUILayout.EndArea();
        }
        #endregion

        #region 事件、对外接口
        public void OnEvent()
        {
            Event e = Event.current;
            if (e.isKey)
            {
                if (e.keyCode == KeyCode.Return && GUI.GetNameOfFocusedControl() == searchBarContentName)
                {
                    root.SetSearchMatchCase(searchStr);
                }
            }
            root.OnEvent();
        }

        public void ReConstructTree(Vector2 position, GraphSurface graphSurface, Graph graph, PortSurface portSurface)
        {
            // 状态清空
            searchStr = "";
            scrollPosition = Vector2.zero;
            root.ResetState();
            ConstructVariableNodes(currentGraph, graph);
            currentGraphSurf = graphSurface;
            currentGraph = graph;


            // TODO 根据传入的上下文Graph,对EventNode进行隐藏

            // TODO 根据传入的上下文Port，对所有Node的isDisabled属性重新赋值
            if (portSurface != null)
            {
                root.SetPortMatchCase(portSurface.target);
            }

            // 清空记录的状态值
            this.position = position;
            PopupWindow.Show(new Rect(position, Vector2.zero), this);
        }

        public void NodeClickHandler(Type nodeType, ValueWrap contextVar = default)
        {
            if (nodeType != null)
            {
                currentGraphSurf.NodeMenuEventHandler(position, nodeType, contextVar);
            }
            editorWindow.Close();
        }
        #endregion

        public override string ToString()
        {
            return root.ToString();
        }

        // 根据传入的上下文 Graph，对VariableNode进行检查，清除与重建
        public void ConstructVariableNodes(Graph oldOne, Graph newOne)
        {
            string path = "Variable/Context/";
            if (oldOne != null)
            {
                foreach (var variable in oldOne.vars)
                {
                    if (!newOne.vars.Contains(variable))
                    {
                        RemoveTreeNode(GetXNode(variable));
                        RemoveTreeNode(SetXNode(variable));
                    }
                }
            }
            if (GetTreeNode("Context") != null) { RemoveTreeNode("Context"); }

            foreach (var variable in newOne.vars)
            {
                // 暂时屏蔽blueprint，没有对应类
                if (variable.type != VarType.Blurprint)
                {
                    AddTreeNode(path + GetXNode(variable), typeof(GetObject<>).MakeGenericType(VariableHelper.Convert(variable.type)), variable);
                    AddTreeNode(path + SetXNode(variable), typeof(SetObject<>).MakeGenericType(VariableHelper.Convert(variable.type)), variable);
                }
            }
        }

        public string GetXNode(ValueWrap variable)
        {
            Type type = VariableHelper.Convert(variable.type);
            return "Get " + variable.name + " [" + type.FriedlyName() + "]";
        }

        public string SetXNode(ValueWrap variable)
        {
            Type type = VariableHelper.Convert(variable.type);
            return "Set " + variable.name + " [" + type.FriedlyName() + "]";

        }
    }
}